package commons.base.rpc.example;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;

import commons.base.netty.http.NettyHttpResponse;
import commons.base.netty.http.NettyHttpServlet;
import commons.base.rpc.RpcInvoker;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.util.CharsetUtil;

public class RpcServlet extends NettyHttpServlet{
	
	private static final Logger log = LogManager.getLogger(RpcServlet.class);
	
	protected RpcInvoker rpcInvoker = new RpcInvoker();
	
	@Override
	public void init() throws Exception {
		
	}
	
	public void addService(Class<?> interfaceClazz, Object impl){
		rpcInvoker.addService(interfaceClazz, impl);
	}
	
	@Override
	public void processRequest(FullHttpRequest request, NettyHttpResponse response){
		// 处理uri
		String[] arr = request.uri().split("/");
		if(arr.length < 4){
			response.renderJson("welcome");
			return ;
		}
		String key = arr[2] + "_" + arr[3];
		if(!rpcInvoker.contains(key)){
			response.renderJson("welcome");
			return ;
		}
				
		String body = null;
		
		if (request.method() == HttpMethod.POST) {
			try {
				body = request.content().toString(CharsetUtil.UTF_8);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}else if(request.method() == HttpMethod.GET){
			
		}else{
			// 不支持其他类型请求
			return ;
		}
		
		// 处理body
		List<Object> args = null;
		if(body != null){
			// 方法形参
			List<Class<?>> ptypes= rpcInvoker.getMethodHandle(key).type().parameterList();
			args = new ArrayList<Object>();
			
			JSONArray jsonArray = JSON.parseArray(body);
			
			for(int i=1; i<ptypes.size(); i++){
				if((i-1) < jsonArray.size()){
					if(ParserConfig.isPrimitive(ptypes.get(i))){
						try {
							args.add(parsePrimitiveParam(jsonArray.get(i-1), ptypes.get(i)));
						} catch (Throwable e) {
							renderError(e, response);
						}
					}else if(Collection.class.isAssignableFrom(ptypes.get(i))){
						args.add(JSON.toJavaObject(jsonArray.getJSONArray(i-1), ptypes.get(i)));
					}else{
						args.add(JSON.toJavaObject(jsonArray.getJSONObject(i-1), ptypes.get(i)));
					}
				}else{
					args.add(null);
				}
			}
		}
		
		try {
			Object result = rpcInvoker.doService(key, args);
			
			JSONObject jsonObject = new JSONObject();
			jsonObject.put(key + "_response", result);
			
			response.renderJson(JSON.toJSONString(jsonObject, SerializerFeature.WriteMapNullValue));
		} catch (Throwable e) {
			renderError(e, response);
		}
	}
	
	/**
	 * 解析基本类型参数
	 * @param raw
	 * @param clazz
	 * @return
	 * @throws ParseException 
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private Object parsePrimitiveParam(Object raw, Class<?> clazz) throws ParseException{
		if(raw == null){
			return null;
		}
		
		if(clazz == Boolean.class || clazz == Boolean.TYPE){
			if(raw instanceof String){
				return Boolean.parseBoolean((String)raw);
			}else{
				return (boolean)raw;
			}
		}else if(clazz == Character.class || clazz == Character.TYPE){
			if(raw instanceof String){
				return ((String)raw).charAt(0);
			}else{
				return (char)raw;
			}
		}else if(clazz == Byte.class || clazz == Byte.TYPE){
			if(raw instanceof String){
				return Byte.parseByte((String)raw);
			}else{
				return (byte)raw;
			}
		}else if(clazz == Short.class || clazz == Short.TYPE){
			if(raw instanceof String){
				return Short.parseShort((String)raw);
			}else{
				return (short)raw;
			}
		}else if(clazz == Integer.class || clazz == Integer.TYPE){
			if(raw instanceof String){
				return Integer.parseInt((String)raw);
			}else{
				return (int)raw;
			}
		}else if(clazz == Long.class || clazz == Long.TYPE){
			if(raw instanceof String){
				return Long.parseLong((String)raw);
			}else{
				return (long)raw;
			}
		}else if(clazz == Float.class || clazz == Float.TYPE){
			if(raw instanceof String){
				return Float.parseFloat((String)raw);
			}else{
				return (float)raw;
			}
		}else if(clazz == Double.class || clazz == Double.TYPE){
			if(raw instanceof String){
				return Double.parseDouble((String)raw);
			}else{
				return (double)raw;
			}
		}else if(clazz == java.util.Date.class){
			if(raw instanceof Long){
				return new Date((long)raw);
			}else if(raw instanceof String){
				return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse((String)raw);
			}else{
				return (Date)raw;
			}
		}else if(clazz.isEnum()){
			if(raw instanceof String){
				return Enum.valueOf((Class<? extends Enum>)clazz, (String)raw);
			}else{
				return (Enum)raw;
			}
		}
		
		return raw;
	}
	
	private void renderError(Throwable e, NettyHttpResponse response){
		log.error(e.getMessage(), e);
		
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("error_response", e.getMessage());
		
		response.renderJson(jsonObject.toJSONString());
	}
	
	@Override
	public void destroy() {
		System.err.println("destroy");
		
	}
	
}